
#include "dversionnumber.h"
#include <QtCore/qhash.h>
//#include <QtCore/private/qlocale_tools_p.h>
//#include <QtCore/qcollator.h>
#include <QLatin1String>
#include <QDebug>
#ifndef QT_NO_DATASTREAM
#  include <QtCore/qdatastream.h>
#endif

#ifndef QT_NO_DEBUG_STREAM
#  include <QtCore/qdebug.h>
#endif

#include <algorithm>
#include <limits>


QVector<int> DVersionNumber::segments() const
{
    if (m_segments.isUsingPointer())
        return *m_segments.pointer_segments;

    QVector<int> result;
    result.resize(segmentCount());
    for (int i = 0; i < segmentCount(); ++i)
        result[i] = segmentAt(i);
    return result;
}

/*!
    \fn int DVersionNumber::segmentAt(int index) const

    Returns the segement value at \a index.  If the index does not exist,
    returns 0.

    \sa segments(), segmentCount()
*/

/*!
    \fn int DVersionNumber::segmentCount() const

    Returns the number of integers stored in segments().

    \sa segments()
*/

/*!
    \fn DVersionNumber DVersionNumber::normalized() const

    Returns an equivalent version number but with all trailing zeros removed.

    To check if two numbers are equivalent, use normalized() on both version
    numbers before performing the compare.

    \snippet dversionnumber/main.cpp 4
 */
DVersionNumber DVersionNumber::normalized() const
{
    int i;
    for (i = m_segments.size(); i; --i)
        if (m_segments.at(i - 1) != 0)
            break;

    DVersionNumber result(*this);
    result.m_segments.resize(i);
    return result;
}

/*!
    \fn bool DVersionNumber::isPrefixOf(const DVersionNumber &other) const

    Returns \c true if the current version number is contained in the \a other
    version number, otherwise returns \c false.

    \snippet dversionnumber/main.cpp 2

    \sa commonPrefix()
*/
bool DVersionNumber::isPrefixOf(const DVersionNumber &other) const
{
    if (segmentCount() > other.segmentCount())
        return false;
    for (int i = 0; i < segmentCount(); ++i) {
        if (segmentAt(i) != other.segmentAt(i))
            return false;
    }
    return true;
}

/*!
    \fn int DVersionNumber::compare(const DVersionNumber &v1,
                                    const DVersionNumber &v2)

    Compares \a v1 with \a v2 and returns an integer less than, equal to, or
    greater than zero, depending on whether \a v1 is less than, equal to, or
    greater than \a v2, respectively.

    Comparisons are performed by comparing the segments of \a v1 and \a v2
    starting at index 0 and working towards the end of the longer list.

    \snippet dversionnumber/main.cpp 1
*/
int DVersionNumber::compare(const DVersionNumber &v1, const DVersionNumber &v2)
{
    int commonlen;

    if (Q_LIKELY(!v1.m_segments.isUsingPointer() && !v2.m_segments.isUsingPointer())) {
        // we can't use memcmp because it interprets the data as unsigned bytes
        const qint8 *ptr1 = v1.m_segments.inline_segments + InlineSegmentStartIdx;
        const qint8 *ptr2 = v2.m_segments.inline_segments + InlineSegmentStartIdx;
        commonlen = qMin(v1.m_segments.size(),
                         v2.m_segments.size());
        for (int i = 0; i < commonlen; ++i)
            if (int x = ptr1[i] - ptr2[i])
                return x;
    } else {
        commonlen = qMin(v1.segmentCount(), v2.segmentCount());
        for (int i = 0; i < commonlen; ++i) {
            if (v1.segmentAt(i) != v2.segmentAt(i))
                return v1.segmentAt(i) - v2.segmentAt(i);
        }
    }

    // ran out of segments in v1 and/or v2 and need to check the first trailing
    // segment to finish the compare
    if (v1.segmentCount() > commonlen) {
        // v1 is longer
        if (v1.segmentAt(commonlen) != 0)
            return v1.segmentAt(commonlen);
        else
            return 1;
    } else if (v2.segmentCount() > commonlen) {
        // v2 is longer
        if (v2.segmentAt(commonlen) != 0)
            return -v2.segmentAt(commonlen);
        else
            return -1;
    }

    // the two version numbers are the same
    return 0;
}

/*!
    DVersionNumber DVersionNumber::commonPrefix(const DVersionNumber &v1,
                                                    const DVersionNumber &v2)

    Returns a version number that is a parent version of both \a v1 and \a v2.

    \sa isPrefixOf()
*/
DVersionNumber DVersionNumber::commonPrefix(const DVersionNumber &v1,
                                            const DVersionNumber &v2)
{
    int commonlen = qMin(v1.segmentCount(), v2.segmentCount());
    int i;
    for (i = 0; i < commonlen; ++i) {
        if (v1.segmentAt(i) != v2.segmentAt(i))
            break;
    }

    if (i == 0)
        return DVersionNumber();

    // try to use the one with inline segments, if there's one
    DVersionNumber result(!v1.m_segments.isUsingPointer() ? v1 : v2);
    result.m_segments.resize(i);
    return result;
}

/*!
    \fn bool operator<(const DVersionNumber &lhs, const DVersionNumber &rhs)
    \relates DVersionNumber

    Returns \c true if \a lhs is less than \a rhs; otherwise returns \c false.

    \sa DVersionNumber::compare()
*/

/*!
    \fn bool operator<=(const DVersionNumber &lhs, const DVersionNumber &rhs)
    \relates DVersionNumber

    Returns \c true if \a lhs is less than or equal to \a rhs; otherwise
    returns \c false.

    \sa DVersionNumber::compare()
*/

/*!
    \fn bool operator>(const DVersionNumber &lhs, const DVersionNumber &rhs)
    \relates DVersionNumber

    Returns \c true if \a lhs is greater than \a rhs; otherwise returns \c
    false.

    \sa DVersionNumber::compare()
*/

/*!
    \fn bool operator>=(const DVersionNumber &lhs, const DVersionNumber &rhs)
    \relates DVersionNumber

    Returns \c true if \a lhs is greater than or equal to \a rhs; otherwise
    returns \c false.

    \sa DVersionNumber::compare()
*/

/*!
    \fn bool operator==(const DVersionNumber &lhs, const DVersionNumber &rhs)
    \relates DVersionNumber

    Returns \c true if \a lhs is equal to \a rhs; otherwise returns \c false.

    \sa DVersionNumber::compare()
*/

/*!
    \fn bool operator!=(const DVersionNumber &lhs, const DVersionNumber &rhs)
    \relates DVersionNumber

    Returns \c true if \a lhs is not equal to \a rhs; otherwise returns
    \c false.

    \sa DVersionNumber::compare()
*/

/*!
    \fn QString DVersionNumber::toString() const

    Returns a string with all of the segments delimited by a period (\c{.}).

    \sa majorVersion(), minorVersion(), microVersion(), segments()
*/
QString DVersionNumber::toString() const
{
    QString version;
    version.reserve(qMax(segmentCount() * 2 - 1, 0));
    bool first = true;
    for (int i = 0; i < segmentCount(); ++i) {
        if (!first)
            version += QLatin1Char('.');
        version += QString::number(segmentAt(i));
        first = false;
    }
    return version;
}

#if QT_STRINGVIEW_LEVEL < 2
/*!
    Constructs a DVersionNumber from a specially formatted \a string of
    non-negative decimal numbers delimited by a period (\c{.}).

    Once the numerical segments have been parsed, the remainder of the string
    is considered to be the suffix string.  The start index of that string will be
    stored in \a suffixIndex if it is not null.

    \snippet DVersionNumber/main.cpp 3

    \sa isNull()
*/
DVersionNumber DVersionNumber::fromString(const QString &string, int *suffixIndex)
{
    return fromString(string.toLatin1(), suffixIndex);
}
#endif


DVersionNumber DVersionNumber::fromString(QByteArray string, int *suffixIndex)
{
    QVector<int> seg;
    int i = 0;
    for (; i < string.size(); i++) {
        qulonglong value = 0;
        if (isdigit(string.at(i))) {
            value = value * 10 + qulonglong(string.at(i) - '0');
            continue;
        }
        if (value > qulonglong(std::numeric_limits<int>::max())) {
            break;
        }
        if (string.at(i) == '.') {
            seg.append(int(value));
            continue;
        }
        break;
    }
    if (suffixIndex) {
        *suffixIndex = string.size() - i;
    }
    return DVersionNumber(std::move(seg));
}

void DVersionNumber::SegmentStorage::setVector(int len, int maj, int min, int mic)
{
    pointer_segments = new QVector<int>;
    pointer_segments->resize(len);
    pointer_segments->data()[0] = maj;
    if (len > 1) {
        pointer_segments->data()[1] = min;
        if (len > 2) {
            pointer_segments->data()[2] = mic;
        }
    }
}

#ifndef QT_NO_DATASTREAM
/*!
   \fn  QDataStream& operator<<(QDataStream &out,
                                const DVersionNumber &version)
   \relates DVersionNumber

   Writes the version number \a version to stream \a out.

   Note that this has nothing to do with QDataStream::version().
 */
QDataStream& operator<<(QDataStream &out, const DVersionNumber &version)
{
    out << version.segments();
    return out;
}

/*!
   \fn QDataStream& operator>>(QDataStream &in, DVersionNumber &version)
   \relates DVersionNumber

   Reads a version number from stream \a in and stores it in \a version.

   Note that this has nothing to do with QDataStream::version().
 */
QDataStream& operator>>(QDataStream &in, DVersionNumber &version)
{
    if (!version.m_segments.isUsingPointer())
        version.m_segments.pointer_segments = new QVector<int>;
    in >> *version.m_segments.pointer_segments;
    return in;
}
#endif

#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug debug, const DVersionNumber &version)
{
    //QDebugStateSaver saver(debug);
    //debug.noquote() << version.toString();
    debug << version.toString();
    return debug;
}
#endif



